/*
    $Header: /Windows/Shell/ShellExt.root/ShellExt/src/CCntxtMenu.cpp 2     08-07-24 0:20 Ken $
    $Log: /Windows/Shell/ShellExt.root/ShellExt/src/CCntxtMenu.cpp $
 * 
 * 2     08-07-24 0:20 Ken
 * add hook key (inject.dll setwindowshookex)
 *
 * 1     08-01-05 0:08 Ken
*/


#include "stdafx.h"

#include "CCntxtMenu.h"
#include "Misc.h"

pfnCreateSubMenuInstance CCntxtMenu::s_pfnCreateSubMenuInstanceS[]=
{
    CCntxtSubMenuCmd::CreateInstance,
    CCntxtSubMenuDoXML::CreateInstance,
    CCntxtSubMenuJunct::CreateInstance,
    CCntxtSubMenuHdlnk::CreateInstance,
};

int CCntxtMenu::s_iSubMenuNumb=sizeof(CCntxtMenu::s_pfnCreateSubMenuInstanceS)/sizeof(CCntxtMenu::s_pfnCreateSubMenuInstanceS[0]);

/////////////////////////////////////////////////////
// CCntxtMenu
//

CCntxtMenu::CCntxtMenu()
{
    m_pSubMenuInfoS=new SubMenuInfo[s_iSubMenuNumb];
    m_pValidSubMenuS=new bool[s_iSubMenuNumb];

    m_iCmdNumb=0;
    for(int i=0; i<s_iSubMenuNumb; i++)
    {
        m_pSubMenuInfoS[i].pSubMenu=(*s_pfnCreateSubMenuInstanceS[i])();
        m_pSubMenuInfoS[i].iStartId=m_iCmdNumb;
        m_pSubMenuInfoS[i].iCmdNumb=m_pSubMenuInfoS[i].pSubMenu->GetMenuNumb();

        m_pValidSubMenuS[i]=false;

        m_iCmdNumb+=m_pSubMenuInfoS[i].iCmdNumb;
    }
}

CCntxtMenu::~CCntxtMenu()
{
    for(int i=0; i<s_iSubMenuNumb; i++)
    {
        delete m_pSubMenuInfoS[i].pSubMenu;
    }

	delete[] m_pSubMenuInfoS;
    delete[] m_pValidSubMenuS;
}

BOOL CCntxtMenu::VTgtInit(TCHAR* pszFl)
{
    BOOL fRet=FALSE;

    memset(m_pValidSubMenuS, 0, sizeof(m_pValidSubMenuS[0])*s_iSubMenuNumb);

    for(int i=0; i<s_iSubMenuNumb; i++)
    {
        if(TRUE==m_pSubMenuInfoS[i].pSubMenu->VTgtInit(pszFl))
        {
            m_pValidSubMenuS[i]=true;
            fRet=TRUE;
        }
    }

    return fRet;
}

BOOL CCntxtMenu::VPreSrcInit(UINT uSrcNumb )
{
    BOOL fRet=FALSE;

    for(int i=0; i<s_iSubMenuNumb; i++)
    {
        if(true==m_pValidSubMenuS[i])
        {
            if(TRUE==m_pSubMenuInfoS[i].pSubMenu->VPreSrcInit(uSrcNumb))
            {
                fRet=TRUE;
            }
            else
            {
                m_pValidSubMenuS[i]=false;
            }
        }
    }

    return fRet;
}

BOOL CCntxtMenu::VSrcInit(UINT uSrcId, TCHAR* pszFl, BOOL fLoad /*called by Load*/)
{
    BOOL fRet=FALSE;

    for(int i=0; i<s_iSubMenuNumb; i++)
    {
        if(true==m_pValidSubMenuS[i])
        {
            if(TRUE==m_pSubMenuInfoS[i].pSubMenu->VSrcInit(uSrcId, pszFl, fLoad ))
            {
                fRet=TRUE;
            }
             else
            {
                m_pValidSubMenuS[i]=false;
            }
        }
    }

    return fRet;
}

BOOL CCntxtMenu::VIsCmdValid(DWORD dwId)
{
    ICntxtSubMenu* pSubMenu;
    DWORD dwSubMenuId;

    if(FALSE==MapCntId2SubMenu(dwId, &pSubMenu, &dwSubMenuId))
    {
        return FALSE;
    }

    return pSubMenu->VIsCmdValid(dwSubMenuId);
}


const TCHAR* CCntxtMenu::VGetMenuString(DWORD dwId)
{
    ICntxtSubMenu* pSubMenu;
    DWORD dwSubMenuId;

    if(FALSE==MapCntId2SubMenu(dwId, &pSubMenu, &dwSubMenuId))
    {
        return NULL;
    }

    return pSubMenu->VGetMenuString(dwSubMenuId);
}

BOOL CCntxtMenu::VInvokeCommand(LPCMINVOKECOMMANDINFO pInfo, DWORD dwId)
{
    ICntxtSubMenu* pSubMenu;
    DWORD dwSubMenuId;

    if(FALSE==MapCntId2SubMenu(dwId, &pSubMenu, &dwSubMenuId))
    {
        return NULL;
    }

    return pSubMenu->VInvokeCommand(pInfo, dwSubMenuId);
}


BOOL  CCntxtMenu::MapCntId2SubMenu(DWORD dwCntId, ICntxtSubMenu** ppSubMenu, DWORD* pdwSubMenuId)
{
    for(int i=0; i<s_iSubMenuNumb; i++)
    {
        if( (dwCntId>=m_pSubMenuInfoS[i].iStartId)
            && (dwCntId<m_pSubMenuInfoS[i].iStartId+m_pSubMenuInfoS[i].iCmdNumb) )
        {
            *ppSubMenu=m_pSubMenuInfoS[i].pSubMenu;
            *pdwSubMenuId=dwCntId-m_pSubMenuInfoS[i].iStartId;
            return TRUE;
        }
    }

    return FALSE;
}


/////////////////////////////////////////////////////
// CCntxtSubMenuCmd
//

const TCHAR* CCntxtSubMenuCmd::s_pszCmdStrS[]=
{
    {_T("Open CMD window")},
    {_T("Execute in CMD window")},
};

int CCntxtSubMenuCmd::s_iCmdNmb=sizeof(CCntxtSubMenuCmd::s_pszCmdStrS)/sizeof(CCntxtSubMenuCmd::s_pszCmdStrS[0]);

BOOL CCntxtSubMenuCmd::VSrcInit(UINT uSrcId, TCHAR* pszFl, BOOL fLoad)
{
    if(0!=uSrcId)
    {
        return FALSE;
    }

    if( FILE_ATTRIBUTE_DIRECTORY & GetFileAttributes( pszFl ) )
    {
        m_strCMD=pszFl;
        m_fValid=TRUE;
    }
    else
    {
        // find the path without file
        for(int i=_tcslen(pszFl)-1; i>0; i--)
        {
            if('\\'==pszFl[i])
            {
                m_strCMD.SetString(pszFl, i);
                m_fValid=TRUE;
                break;
            }
        }

        // if executable file
        if( (0==_tcsicmp(_T(".bat"), PathFindExtension(pszFl) ) )
            || (0==_tcsicmp(_T(".exe"), PathFindExtension(pszFl) ) ) )
        {
            m_strCMDExec=pszFl;
            m_fValid=TRUE;
        }

    }

    return m_fValid;
}

BOOL CCntxtSubMenuCmd::VIsCmdValid(DWORD dwId)
{
    if( (dwId>=s_iCmdNmb)
        || ( FALSE==m_fValid ) )
    {
        return FALSE;
    }

    if( (1==dwId)
        && (m_strCMDExec.IsEmpty()) )
    {
        return FALSE;
    }

    return TRUE;
}

BOOL CCntxtSubMenuCmd::VInvokeCommand(LPCMINVOKECOMMANDINFO pInfo, DWORD dwId)
{
    if( (1<dwId)
        || ( FALSE==m_fValid ) )
    {
         return FALSE;
    }

    TCHAR szCMDLine[MAX_PATH];
    if(0==dwId)
    {
        _stprintf_s(szCMDLine, _T("/k cd /d \"%s\\\""), m_strCMD.GetString());
        ShellExecute(NULL, _T("open"), _T("cmd.exe"), szCMDLine, m_strCMD, SW_SHOWNORMAL);
    }
    else if(1==dwId)
    {
        _stprintf_s(szCMDLine, _T("/k cd /d \"%s\\\" | \"%s\""), m_strCMD.GetString(), m_strCMDExec.GetString());
        ShellExecute(NULL, _T("open"), _T("cmd.exe"), szCMDLine, m_strCMDExec, SW_SHOWNORMAL);
    }
    return TRUE;
}

ICntxtSubMenu* CCntxtSubMenuCmd::CreateInstance()
{
    return new CCntxtSubMenuCmd;
}


/////////////////////////////////////////////////////
// CCntxtSubMenuDoXML
//

const TCHAR* CCntxtSubMenuDoXML::s_pszCmdStrS[]=
{
    {_T("Do ShellExt XML Operations")}
};

int CCntxtSubMenuDoXML::s_iCmdNmb=sizeof(CCntxtSubMenuDoXML::s_pszCmdStrS)/sizeof(CCntxtSubMenuDoXML::s_pszCmdStrS[0]);

BOOL CCntxtSubMenuDoXML::VSrcInit(UINT uSrcId, TCHAR* pszFl, BOOL fLoad)
{
    if( (0==uSrcId)
        && (!( FILE_ATTRIBUTE_DIRECTORY & GetFileAttributes( pszFl ) ) )
        && ( 0 == _tcsicmp( _T( "ShellExt.xml" ), PathFindFileName( pszFl ) ) ))
    {
        m_strXMLFile=pszFl;
        m_fValid=TRUE;
        return TRUE;
    }

    return FALSE;
}

BOOL CCntxtSubMenuDoXML::VInvokeCommand(LPCMINVOKECOMMANDINFO pInfo, DWORD dwId)
{
    if( (0==dwId)
        && ( FALSE!=m_fValid ) )
    {
        CShellExtXML cshlextXML;
        if(FALSE==cshlextXML.Init(m_strXMLFile, FALSE))
        {
            TCHAR    szMessage[512];
            _stprintf_s(szMessage, _T("invalid shellext xml file: %s!"), m_strXMLFile);
            MessageBox(pInfo->hwnd, szMessage, _T("Do ShellExt XML Ops error,\r\ninvalide shellext xml file!"), MB_OK);
            return FALSE;
        }
        cshlextXML.DoShellExtXML();
        return TRUE;
    }

    return FALSE;
}

ICntxtSubMenu* CCntxtSubMenuDoXML::CreateInstance()
{
    return new CCntxtSubMenuDoXML;
}


/////////////////////////////////////////////////////
// CCntxtSubMenuJunct
//

const TCHAR* CCntxtSubMenuJunct::s_pszCmdStrS[]=
{
    {_T("Create Junction(s)")},			// n src dirs   1 target dir
    {_T("Delete Junction(s)")},			// n src juncts 0 target
    {_T("Jump to Junction Target")},	// 1 src juncts 0 target
    {_T("Open Junction Target")},		// 1 src juncts 0 target
};

int CCntxtSubMenuJunct::s_iCmdNmb=sizeof(CCntxtSubMenuJunct::s_pszCmdStrS)/sizeof(CCntxtSubMenuJunct::s_pszCmdStrS[0]);

BOOL CCntxtSubMenuJunct::VTgtInit(TCHAR* pszFl)
{
    m_strTgt=pszFl;
    return TRUE;
}

BOOL CCntxtSubMenuJunct::VSrcInit(UINT uSrcId, TCHAR* pszFl, BOOL fLoad)
{
    // all src must be directory
    if( (FALSE==m_fValid)
        || (!( FILE_ATTRIBUTE_DIRECTORY & GetFileAttributes( pszFl ) ) ) )
    {
        m_fValid=FALSE;
        return FALSE;
    }

    m_lststrSrcDir.push_back(pszFl);

    CJunction cjunct;
    if( FALSE != cjunct.Initialize( pszFl ) )
    {
        m_lstJunct.push_back(cjunct);
    }
    else
    {
        if(m_strTgt.IsEmpty())
        {
            m_fValid=FALSE;
            return FALSE;
        }
    }

    return TRUE;
}

BOOL CCntxtSubMenuJunct::VIsCmdValid(DWORD dwId)
{
    BOOL fRet=FALSE;

    if( (s_iCmdNmb<=dwId)
        || ( FALSE==m_fValid ) )
    {
        return FALSE;
    }

    switch (dwId)
    {
//    {_T("Create Junction(s)")},			// n src dirs   1 target dir
    case 0:
        if( (!m_strTgt.IsEmpty())
            && (0!=m_lststrSrcDir.size()) )
        {
            fRet=TRUE;
        }
        break;

//    {_T("Delete Junction(s)")},			// n src juncts 0 target
    case 1:
        if( (m_strTgt.IsEmpty())
            && 0!=m_lstJunct.size()
            && m_lstJunct.size()==m_lststrSrcDir.size() )
        {
            fRet=TRUE;
        }
        break;

//    {_T("Jump to Junction Target")},	// 1 src juncts 0 target
//    {_T("Open Junction Target")},		// 1 src juncts 0 target
    case 2:
    case 3:
        if( (m_strTgt.IsEmpty())
            && 1==m_lstJunct.size()
            && 1==m_lststrSrcDir.size() )
        {
            fRet=TRUE;
        }
        break;

    default:
		break;
    }

    return fRet;
}

BOOL CCntxtSubMenuJunct::VInvokeCommand(LPCMINVOKECOMMANDINFO pInfo, DWORD dwId)
{
    if( (s_iCmdNmb<=dwId)
        || ( FALSE==m_fValid ) )
    {
        return FALSE;
    }

    BOOL fRet=TRUE;

    switch (dwId)
    {
//    {_T("Create Junction(s)")},			// n src dirs   1 target dir
    case 0:
        fRet=DoCreateJunct();
        break;

//    {_T("Delete Junction(s)")},			// n src juncts 0 target
    case 1:
        fRet=DoDeleteJunct();
        break;

//    {_T("Jump to Junction Target")},	// 1 src juncts 0 target
    case 2:
        ShellExecute(pInfo->hwnd, _T("explore"), m_lstJunct.front().GetJunctionTgt(), NULL, NULL, SW_SHOWNORMAL);
        break;

//    {_T("Open Junction Target")},		// 1 src juncts 0 target
    case 3:
        ShellExecute(NULL, _T("explore"), m_lstJunct.front().GetJunctionTgt(), NULL, NULL, SW_SHOWNORMAL);
        break;

    default:
        fRet=FALSE;
    }

    return fRet;
}

BOOL CCntxtSubMenuJunct::DoCreateJunct()
{
    int iMsbRet;
    TCHAR szUniqueName[MAX_PATH];
    TCHAR szMessage[512];

    int iDefExistSel=0;
    int iDefCreateSel=0;
    for ( list<CString>::iterator it=m_lststrSrcDir.begin(); it!= m_lststrSrcDir.end(); it++ )
    {
        if(FALSE!=MakeUniqueFileName(szUniqueName, ARRAYOF(szUniqueName), *it, m_strTgt))
        {
            if( PathFileExists ( szUniqueName ) )
            {
                if(0==iDefExistSel)
                {
                    _stprintf_s(szMessage, _T("The target name had already exist! Continue?\r\nName: %s\r\n\r\nPress shift to make as default select!"), *it );
                    iMsbRet=MessageBox ( NULL, szMessage, _T("CreateJunction Warnning!"), MB_YESNOCANCEL );
                    if( (0x8000&GetAsyncKeyState(VK_SHIFT))!=0)
                    {
                        iDefExistSel=iMsbRet;
                    }
                }
                else
                {
                    iMsbRet=iDefExistSel;
                }

                if ( IDNO==iMsbRet)
                {
                    continue;
                }
                else if(IDCANCEL==iMsbRet)
                {
                    return FALSE;
                }
            }
        }

        if(FALSE==CJunction::CreateJunction(szUniqueName, *it, TRUE))
        {
            if(0==iDefCreateSel)
            {
                _stprintf_s(szMessage, _T("Create junction %s to target:%s faild! continue?\r\n\r\nPress shift to make as default select!"), *it, m_strTgt);
                iMsbRet=MessageBox(NULL, szMessage, _T("CreateJunction Warnning!"), MB_OKCANCEL);
                if( (0x8000&GetAsyncKeyState(VK_SHIFT))!=0)
                {
                    iDefCreateSel=iMsbRet;
                }
            }
            else
            {
               iMsbRet=iDefCreateSel;
            }

            if(IDOK!=iMsbRet)
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}

BOOL CCntxtSubMenuJunct::DoDeleteJunct()
{
    int iMsbRet;
    TCHAR szMessage[512];

    int iDefSel=0;
    for ( list<CJunction>::iterator it=m_lstJunct.begin(); it!= m_lstJunct.end(); it++ )
    {
        if(FALSE==it->DelJunction())
        {
            if(0!=iDefSel)
            {
                _stprintf_s(szMessage, _T("Del junction %s faild! Continue?\r\n\r\nPress shift to make as default select!"), it->GetJunctionName());
                iMsbRet=MessageBox(NULL, szMessage, _T("DelJunct Warnning!"), MB_YESNO);
                if( (0x8000&GetAsyncKeyState(VK_SHIFT))!=0)
                {
                    iDefSel=iMsbRet;
                }
            }
            else
            {
                iMsbRet=iDefSel;
            }

            if(IDYES!=iMsbRet)
            {
                return FALSE;
            }
        }
    }

    return TRUE;
}

ICntxtSubMenu* CCntxtSubMenuJunct::CreateInstance()
{
    return new CCntxtSubMenuJunct;
}


/////////////////////////////////////////////////////
// CCntxtSubMenuHdlnk
//

const TCHAR* CCntxtSubMenuHdlnk::s_pszCmdStrS[]=
{
    {_T("Create hardlink(s)")}
};

int CCntxtSubMenuHdlnk::s_iCmdNmb=sizeof(CCntxtSubMenuHdlnk::s_pszCmdStrS)/sizeof(CCntxtSubMenuHdlnk::s_pszCmdStrS[0]);

BOOL CCntxtSubMenuHdlnk::VSrcInit(UINT uSrcId, TCHAR* pszFl, BOOL fLoad)
{
    if( FILE_ATTRIBUTE_DIRECTORY & GetFileAttributes( pszFl ) )
    {
        m_fValid=FALSE;
        return FALSE;
    }

    m_fValid=TRUE;
    m_lststrSrcFl.push_back(pszFl);

    return TRUE;
}

BOOL CCntxtSubMenuHdlnk::VInvokeCommand(LPCMINVOKECOMMANDINFO pInfo, DWORD dwId)
{
    if( (0!=dwId)
        || ( FALSE==m_fValid ) )
    {
        return FALSE;
    }

    int iMsbRet;
    TCHAR szUniqueName[MAX_PATH];
    TCHAR szMessage[512];

    int iDefExistSel=0;
    int iDefCreateSel=0;

    for ( list<CString>::iterator it=m_lststrSrcFl.begin(); it!= m_lststrSrcFl.end(); it++ )
    {
        if(FALSE!=MakeUniqueFileName(szUniqueName, ARRAYOF(szUniqueName), *it, m_strTgt))
        {
            if( PathFileExists ( szUniqueName ) )
            {
                if(0==iDefExistSel)
                {
                    _stprintf_s(szMessage, _T("The target name had already exist! Replace it?\r\nName: %s\r\n\r\nPress shift to make as default select!"), *it );
                    iMsbRet=MessageBox ( NULL, szMessage, _T("CreateHardLink Warnning!"), MB_YESNOCANCEL );
                    if( (0x8000&GetAsyncKeyState(VK_SHIFT))!=0)
                    {
                        iDefExistSel=iMsbRet;
                    }
                }
                else
                {
                    iMsbRet=iDefExistSel;
                }

                if ( IDNO==iMsbRet)
                {
                    continue;
                }
                else if(IDCANCEL==iMsbRet)
                {
                    return FALSE;
                }
            }

            if ( !CreateHardLink ( szUniqueName, *it, NULL ))
            {
                if(0!=iDefCreateSel)
                {
                    _stprintf_s(szMessage, _T("Create Hardlink %s to target:%s faild! continue?\r\n\r\nPress shift to make as default select!"), *it, m_strTgt);
                    iMsbRet=MessageBox(NULL, szMessage, _T("CreateHardLink Warnning!"), MB_OKCANCEL);
                    if( (0x8000&GetAsyncKeyState(VK_SHIFT))!=0)
                    {
                        iDefCreateSel=iMsbRet;
                    }
                }
                else
                {
                   iMsbRet=iDefCreateSel;
                }

                if(IDOK!=iMsbRet)
                {
                    return FALSE;
                }
            }
        }
    }

    return TRUE;
}

ICntxtSubMenu* CCntxtSubMenuHdlnk::CreateInstance()
{
    return new CCntxtSubMenuHdlnk;
}


